/**
	A class for the fastest possible Iterable in the given platform.
	Currently, FastList is the fastest for Flash9/10, and Array is fastest
	for everything else.  Unfortunately, FastList pops and add from the head of 
	the Iterable, while Array pops from the tail. FastestIterable handles this by 
	providing a semantically clear version of pop and add (i.e. popHead() and popTail()), 
	while	also providing a method for the fastest possible approach (i.e. popFast()). 
	Basically, if you're not worried about the order of elements in the Iterable, 
	use the popFast, addFast, etc. methods.  Otherwise, use the other methods that 
	clearly define how they are modifying the Iterable.
**/

class FastestIterable<A>{
/**
	The main Iterable variable, this will be a FastList for Flash (as shown in the API), 
	or an Array for anything else. This is determined through conditional compilation.
**/
#if flash
   public var it:FastList<A>;
#else
	var it:Array<A>;
#end

/**
	Indicates if the Iterable is Array based or not.
**/
	public var arrayBased(default,null):Bool;
	
/**
	Returns the length of the Iterable
**/
	public var length(default,null):Int;

/**
	The constructor.  Optionally lets you initialize a FastestIterable from an existing Iterable.
**/
	public function new(?load_it:Iterable<A>){
		length = 0;
#if flash
   	it = new FastList<A>();
		if (load_it != null) { 
			length = IterTools.getLength(load_it);
			it = ListTools.fastList(load_it);
		}
		
		arrayBased = false;
#else
		it = new Array<A>();
		if (load_it != null) it = ArrayTools.fastCreateArray(load_it);
		length = it.length;
		arrayBased = true;
#end	
		
	}
	
/**
	Returns an Iterator.
**/
	public function iterator(){
		return it.iterator();
	}
	
/**
	Removes the given element from the Iterable.  Return true if successful, false if not.
**/	
	public function remove(e:A):Bool{
		var removed = it.remove(e);
#if flash
		if (removed) length -=1;
#else
		length = it.length;
#end
		return removed;	
	}

/**
	Removes the given element from the Iterable as fast as possible. For FastList based Iterables, this
	is the same as remove().  For Arrays, the method uses ArrayTools.swapAndPop() which will usually
	change the ordering of elements in the Iterable. 
	Return true if successful, false if not.
**/	
	public function removeFast(e:A):Bool{
#if flash
		return it.remove(e);
#else 
		var result =  ArrayTools.swapAndPop(it, e);	
		length = it.length;
		return result;	
#end		
	}
	
/**
	A consistent String based representation of the Iterable, identical in formatting to how Lists print.
**/		
	public function toString(){
		var str_buf:String ="";
		str_buf += "{";
		for (i in it){
			str_buf += i + ', ';
		}
		str_buf = str_buf.substr(0, str_buf.length - 2);
		str_buf += "}";
		return str_buf;
	}


/**
	Returns true if Iterable is empty, false otherwise.
**/	
	public function isEmpty():Bool{
#if flash
   	return it.isEmpty();
#else
		return length == 0;
#end		
	}

/**
	Returns the first element of the Iterable.
**/	
	public function first(){
#if flash
   	return it.first();
#else
		return it[0];
#end		
	}

/**
	Pops the tail of the Iterable.  
**/	
	public function popTail():A{
#if flash
		var previous:FastCell<A> = null;	
		var current = it.head;
		while(current != null){
			if (current.next != null) {
				previous = current;
				current = current.next;
			} else break;
		}
		if (previous == null) return null;
		var result = previous.next;
		previous.next = null;
		return result.elt;
#else
		return it.pop();
#end
	}


/**
	Pops the Iterable, always removing from the head.
**/	
	public function popHead():A{
#if flash
   	return it.pop();
#else
		return it.shift();
#end		
	}
	
/**
	Pops the Iterable from whichever end is fastest.
**/	
	public function popFast():A{
		return it.pop();
	}
	
/**
	Adds to the Iterable at the head.
**/	
	public function addHead(e:A){
#if flash
   	it.add(e);
#else
		it.unshift(e);
#end
	}


/**
	Adds to the Iterable at the tail.
**/	
	public function addTail(e:A){
#if flash
		var current = it.head;
   	while(current.next != null){
			current = current.next;
		}
		var c = new FastCell<A>(e,null);
		current.next = c;
#else
		dit.push(e);
#end		
	}

/**
	Adds to the Iterable at whichever end is fastest.
**/
	public function addFast(e:A){
#if flash
   	it.add(e);
#else
		it.push(e);
#end	
	}
	
}
